package com.zmn.plat.common.util;

import com.google.common.collect.Lists;
import com.zmn.common.utils.date.DateUtil;
import org.apache.http.util.Asserts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 比较相同类的两个对象, 返回不一致属性名称与值
 *
 * @author xiangjunming
 * @since 2020/07/17 10:52:35
 */
public class CompareObjectUtil {

    private static Logger logger = LoggerFactory.getLogger(CompareObjectUtil.class);

    private static final List<String> notLogFieldList;

    static {
        notLogFieldList= Lists.newArrayList();
        // 邀评说明
        notLogFieldList.add("inviteCommentExplain");
        // 支付说明
        notLogFieldList.add("payExplain");
    }

    /**
     * 获取不需要记录日志变化字段
     * @return
     */
    public static List<String> getNotLogFieldList(){
        return notLogFieldList;
    }


    /**
     * 比较相同类的两个对象, 返回不一致属性名称与值
     *
     * @param <T>    the type parameter
     * @param newObj the new obj
     * @param oldObj the old obj
     * @return the list
     */
    public static <T> List<CompareField> compareObj(T newObj, T oldObj) {
        Asserts.notNull(newObj, "newObj not null");
        Asserts.notNull(newObj, "oldObj not null");

        if (newObj == oldObj || newObj.equals(oldObj)) {
            return Collections.emptyList();
        }

        List<CompareField> compareFields = new ArrayList<>();

        Class<?> tClass = newObj.getClass();
        Field[] fields = tClass.getDeclaredFields();
        try {
            for (Field field : fields) {
                FieldNameMapping fnm = field.getAnnotation(FieldNameMapping.class);
                if (fnm != null) {
                    PropertyDescriptor pd = new PropertyDescriptor(field.getName(), tClass);
                    Method getMethod = pd.getReadMethod();
                    Object newValue = getMethod.invoke(newObj);
                    Object oldValue = getMethod.invoke(oldObj);
                    if (oldValue instanceof Date) {
                        oldValue = DateUtil.dateFormatToString((Date) oldValue, DateUtil.FORMAT_DEFAULT);
                    }
                    if (newValue instanceof Date) {
                        newValue = DateUtil.dateFormatToString((Date) newValue, DateUtil.FORMAT_DEFAULT);
                    }
                    if (!Objects.equals(newValue, oldValue)) {
                        if (notLogFieldList.contains(field.getName())) {
                            compareFields.add(new CompareField(field.getName(), fnm.value(), "", ""));
                            continue;
                        }
                        compareFields.add(new CompareField(field.getName(), fnm.value(), oldValue, newValue));
                    }
                }
            }
        } catch (IllegalAccessException e) {
            logger.error("error:", e);
        } catch (InvocationTargetException e) {
            logger.error("error:", e);
        } catch (IntrospectionException e) {
            logger.error("error:", e);
        }
        return compareFields;
    }


    /**
     * The type Compare field.
     *
     * @author xiangjunming
     * @since 2020/07/17 10:52:35
     */
    public static class CompareField {
        private String field;
        private String fieldName;
        private Object oldValue;
        private Object newValue;

        public CompareField() {
        }

        public CompareField(String field, String fieldName, Object oldValue, Object newValue) {
            this.field = field;
            this.fieldName = fieldName;
            this.oldValue = oldValue;
            this.newValue = newValue;
        }

        public String getField() {
            return field;
        }

        public void setField(String field) {
            this.field = field;
        }

        public String getFieldName() {
            return fieldName;
        }

        public void setFieldName(String fieldName) {
            this.fieldName = fieldName;
        }

        public Object getOldValue() {
            return oldValue;
        }

        public void setOldValue(Object oldValue) {
            this.oldValue = oldValue;
        }

        public Object getNewValue() {
            return newValue;
        }

        public void setNewValue(Object newValue) {
            this.newValue = newValue;
        }
    }
}
